package com.chen.blue.interceptor.datapermission;

import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.core.toolkit.PluginUtils;
import com.baomidou.mybatisplus.extension.parser.JsqlParserSupport;
import com.baomidou.mybatisplus.extension.plugins.inner.InnerInterceptor;
import com.chen.blue.container.PermissionFlag;
import com.chen.blue.dictionary.AuditDict;
import com.chen.blue.dictionary.DataPermissionDict;
import com.chen.blue.dictionary.SystemInnerDict;
import com.chen.blue.oa.status.OaStatus;
import com.chen.blue.service.security.SecurityService;
import lombok.extern.slf4j.Slf4j;
import net.sf.jsqlparser.JSQLParserException;
import net.sf.jsqlparser.expression.Expression;
import net.sf.jsqlparser.expression.LongValue;
import net.sf.jsqlparser.expression.StringValue;
import net.sf.jsqlparser.expression.operators.conditional.AndExpression;
import net.sf.jsqlparser.expression.operators.relational.EqualsTo;
import net.sf.jsqlparser.expression.operators.relational.ExpressionList;
import net.sf.jsqlparser.expression.operators.relational.InExpression;
import net.sf.jsqlparser.parser.CCJSqlParserUtil;
import net.sf.jsqlparser.schema.Column;
import net.sf.jsqlparser.statement.delete.Delete;
import net.sf.jsqlparser.statement.insert.Insert;
import net.sf.jsqlparser.statement.select.*;
import net.sf.jsqlparser.statement.update.Update;
import org.apache.ibatis.executor.Executor;
import org.apache.ibatis.mapping.BoundSql;
import org.apache.ibatis.mapping.MappedStatement;
import org.apache.ibatis.mapping.ParameterMapping;
import org.apache.ibatis.session.ResultHandler;
import org.apache.ibatis.session.RowBounds;

import java.sql.SQLException;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;

/**
 * Created By CLT On 2023/12/5.
 */
@SuppressWarnings("all")
@Slf4j
public class PermissionInterceptor extends JsqlParserSupport implements InnerInterceptor {

    /**
     * 数据权限类型(0全部数据权限，1本部门数据权限，2本部门及部门以下数据权限，3自定义数据权限，4仅个人数据权限)
     */

    /**
     * 已配数据权限的表：1. 系统用户模块 2. 系统菜单模块 3. 系统部门模块 4. 业务审核记录模块 5. 业务赛事列表模块 6. 业务赛事申请模块
     */

    /**
     * 制定数据权限规则
     */
    private final Map<String, Map<Integer, Expression>> rulesCache = new ConcurrentHashMap<>(6);
    private Map<String, Integer> auditRulesCache = null;
    private Map<Integer, Expression> rule = null;

    protected void initRules() {

        /**
         * 系统管理
         */
        rulesCache.put(DataPermissionDict.TABLE_NAME_SYSTEM_USER, setRule(DataPermissionDict.TABLE_NAME_SYSTEM_USER));
        rulesCache.put(DataPermissionDict.TABLE_NAME_SYSTEM_MENU, setRule(DataPermissionDict.TABLE_NAME_SYSTEM_MENU));
        rulesCache.put(DataPermissionDict.TABLE_NAME_SYSTEM_DEPT, setRule(DataPermissionDict.TABLE_NAME_SYSTEM_DEPT));

        /**
         * 业务管理
         */
        rulesCache.put(DataPermissionDict.TABLE_NAME_SERVICE_CONTEST_APPLY_ACTIVE, setRule(DataPermissionDict.TABLE_NAME_SERVICE_CONTEST_APPLY_ACTIVE));
        rulesCache.put(DataPermissionDict.TABLE_NAME_SERVICE_CONTEST_APPLY, setRule(DataPermissionDict.TABLE_NAME_SERVICE_CONTEST_APPLY));
        rulesCache.put(DataPermissionDict.TABLE_NAME_SERVICE_CONTEST_APPLY_RECORD, setRule(DataPermissionDict.TABLE_NAME_SERVICE_CONTEST_APPLY_RECORD));

    }

    protected void initAuditRules() {

        OaStatus oaStatus = new OaStatus();
        auditRulesCache = oaStatus.getAuditRules();

    }

    protected Map<Integer, Expression> setRule(String tableName) {

        if (DataPermissionDict.TABLE_NAME_SYSTEM_USER.equals(tableName)) {

            rule = new HashMap<>(4);
            rule.put(DataPermissionDict.DATA_SCOPE_DEPT_OWN, createEq(DataPermissionDict.DATA_SCOPE_COLUMN_DEPT_ID, getDeptId(), tableName));
            rule.put(DataPermissionDict.DATA_SCOPE_DEPT_CHILDREN, createIn(DataPermissionDict.DATA_SCOPE_COLUMN_DEPT_ID, getDeptChildrenIds(), tableName));
            rule.put(DataPermissionDict.DATA_SCOPE_DEPT_SELECT, createIn(DataPermissionDict.DATA_SCOPE_COLUMN_DEPT_ID, getSelectedDeptIds(), tableName));
            rule.put(DataPermissionDict.DATA_SCOPE_OWN, createEq(DataPermissionDict.DATA_SCOPE_COLUMN_ID, getUserId(), tableName));

        }

        if (DataPermissionDict.TABLE_NAME_SYSTEM_MENU.equals(tableName)) {

            rule = new HashMap<>(4);
            rule.put(DataPermissionDict.DATA_SCOPE_DEPT_OWN, createIn(DataPermissionDict.DATA_SCOPE_COLUMN_ID, getMenuIds(), tableName));
            rule.put(DataPermissionDict.DATA_SCOPE_DEPT_CHILDREN, createIn(DataPermissionDict.DATA_SCOPE_COLUMN_ID, getMenuIds(), tableName));
            rule.put(DataPermissionDict.DATA_SCOPE_DEPT_SELECT, createIn(DataPermissionDict.DATA_SCOPE_COLUMN_ID, getMenuIds(), tableName));
            rule.put(DataPermissionDict.DATA_SCOPE_OWN, createIn(DataPermissionDict.DATA_SCOPE_COLUMN_ID, getMenuIds(), tableName));

        }

        if (DataPermissionDict.TABLE_NAME_SYSTEM_DEPT.equals(tableName)) {

            rule = new HashMap<>(4);
            rule.put(DataPermissionDict.DATA_SCOPE_DEPT_OWN, createEq(DataPermissionDict.DATA_SCOPE_COLUMN_ID, getDeptId(), tableName));
            rule.put(DataPermissionDict.DATA_SCOPE_DEPT_CHILDREN, createIn(DataPermissionDict.DATA_SCOPE_COLUMN_ID, getDeptChildrenIds(), tableName));
            rule.put(DataPermissionDict.DATA_SCOPE_DEPT_SELECT, createIn(DataPermissionDict.DATA_SCOPE_COLUMN_ID, getSelectedDeptIds(), tableName));
            rule.put(DataPermissionDict.DATA_SCOPE_OWN, createEq(DataPermissionDict.DATA_SCOPE_COLUMN_ID, getDeptId(), tableName));

        }

        if (DataPermissionDict.TABLE_NAME_SERVICE_CONTEST_APPLY_ACTIVE.equals(tableName)) {

            rule = new HashMap<>(1);
            rule.put(DataPermissionDict.DATA_SCOPE_DEPT_OWN, createInAndIn(DataPermissionDict.DATA_SCOPE_COLUMN_PERSON_AUDIT, DataPermissionDict.DATA_SCOPE_COLUMN_CREATOR, getPersonAuditCode(), getUserNameByCurrentDept(), tableName));
            rule.put(DataPermissionDict.DATA_SCOPE_DEPT_CHILDREN, createInAndIn(DataPermissionDict.DATA_SCOPE_COLUMN_PERSON_AUDIT, DataPermissionDict.DATA_SCOPE_COLUMN_CREATOR, getPersonAuditCode(), creatorsActiveChildDept(), tableName));
            rule.put(DataPermissionDict.DATA_SCOPE_DEPT_SELECT, createInAndIn(DataPermissionDict.DATA_SCOPE_COLUMN_PERSON_AUDIT, DataPermissionDict.DATA_SCOPE_COLUMN_CREATOR, getPersonAuditCode(), creatorsActiveSelectDept(), tableName));
            rule.put(DataPermissionDict.DATA_SCOPE_OWN, createInAndIn(DataPermissionDict.DATA_SCOPE_COLUMN_PERSON_AUDIT, DataPermissionDict.DATA_SCOPE_COLUMN_CREATOR, getPersonAuditCode(), Arrays.asList(getCurrentUserName()), tableName));

        }

        if (DataPermissionDict.TABLE_NAME_SERVICE_CONTEST_APPLY.equals(tableName)) {

            // 数据权限暂时只支持个人
            rule = new HashMap<>(4);
            rule.put(DataPermissionDict.DATA_SCOPE_DEPT_OWN, createEq(DataPermissionDict.DATA_SCOPE_COLUMN_CREATOR, getCurrentUserName(), tableName));
            rule.put(DataPermissionDict.DATA_SCOPE_DEPT_CHILDREN, createEq(DataPermissionDict.DATA_SCOPE_COLUMN_CREATOR, getCurrentUserName(), tableName));
            rule.put(DataPermissionDict.DATA_SCOPE_DEPT_SELECT, createEq(DataPermissionDict.DATA_SCOPE_COLUMN_CREATOR, getCurrentUserName(), tableName));
            rule.put(DataPermissionDict.DATA_SCOPE_OWN, createEq(DataPermissionDict.DATA_SCOPE_COLUMN_CREATOR, getCurrentUserName(), tableName));

        }

        if (DataPermissionDict.TABLE_NAME_SERVICE_CONTEST_APPLY_RECORD.equals(tableName)) {

            rule = new HashMap<>(4);
            rule.put(DataPermissionDict.DATA_SCOPE_DEPT_OWN, createIn(DataPermissionDict.DATA_SCOPE_COLUMN_CREATOR, getUserNameByCurrentDept(), tableName));
            rule.put(DataPermissionDict.DATA_SCOPE_DEPT_CHILDREN, createIn(DataPermissionDict.DATA_SCOPE_COLUMN_CREATOR, getUserNameByDeptIds(), tableName));
            rule.put(DataPermissionDict.DATA_SCOPE_DEPT_SELECT, createIn(DataPermissionDict.DATA_SCOPE_COLUMN_CREATOR, getUserNameBySelectDeptIds(), tableName));
            rule.put(DataPermissionDict.DATA_SCOPE_OWN, createEq(DataPermissionDict.DATA_SCOPE_COLUMN_CREATOR, getCurrentUserName(), tableName));

        }

        return rule;

    }

    protected Expression createEq(String columnName, String value, String tableName) {

        if (Objects.isNull(value)) {
            log.warn(StrUtil.format("{}->数据权限构建表达式失败，规则字段：{}->{} 无效"), tableName, columnName, value);
            return null;
        }

        EqualsTo equalsTo = new EqualsTo();
        equalsTo.setLeftExpression(new Column(columnName));
        equalsTo.setRightExpression(new StringValue(value));
        return equalsTo;

    }

    protected Expression createEq(String columnName, Long value, String tableName) {

        if (Objects.isNull(value)) {
            log.warn(StrUtil.format("{}->数据权限构建表达式失败，规则字段：{}->{} 无效"), tableName, columnName, value);
            return null;
        }

        EqualsTo equalsTo = new EqualsTo();
        equalsTo.setLeftExpression(new Column(columnName));
        equalsTo.setRightExpression(new LongValue(value));
        return equalsTo;

    }

    protected Expression createFail() throws JSQLParserException {

        return CCJSqlParserUtil.parseCondExpression("1=2");

    }

    protected Expression createIn(String columnName, Set<Long> value, String tableName) {

        if (Objects.isNull(value) || value.isEmpty()) {
            log.warn(StrUtil.format("{}->数据权限构建表达式失败，规则字段：{}->{} 无效"), tableName, columnName, value);
            return null;
        }

        ExpressionList expressionList = new ExpressionList();
        for (Long aLong : value) {
            expressionList.addExpressions(new LongValue(aLong));
        }

        InExpression deptIdsIn = new InExpression(new Column(columnName), expressionList);
        return deptIdsIn;

    }

    protected Expression createInAndIn(String columnNameOne, String columnNameTwo, Set<Long> valueOne, List<String> valueTwo, String tableName) {

        if (Objects.isNull(valueOne) || valueOne.isEmpty() || Objects.isNull(valueTwo) || valueTwo.isEmpty()) {
            log.warn(StrUtil.format("{}->数据权限构建表达式失败，规则字段：{}->{} 无效"), tableName, columnNameOne, valueOne);
            return null;
        }

        ExpressionList expressionList = new ExpressionList();
        for (Long aLong : valueOne) {
            expressionList.addExpressions(new LongValue(aLong));
        }

        InExpression deptIdsIn = new InExpression(new Column(columnNameOne), expressionList);


        ExpressionList expressionListTwo = new ExpressionList();
        for (String username : valueTwo) {
            expressionListTwo.addExpressions(new StringValue(username));
        }

        InExpression deptIdsInTwo = new InExpression(new Column(columnNameTwo), expressionListTwo);

        AndExpression andExpression = new AndExpression(deptIdsIn, deptIdsInTwo);

        return andExpression;

    }

    protected Expression createIn(String columnName, List<String> value, String tableName) {

        if (Objects.isNull(value) || value.isEmpty()) {
            log.warn(StrUtil.format("{}->数据权限构建表达式失败，规则字段：{}->{} 无效"), tableName, columnName, value);
            return null;
        }

        ExpressionList expressionList = new ExpressionList();
        for (String username : value) {
            expressionList.addExpressions(new StringValue(username));
        }

        InExpression deptIdsIn = new InExpression(new Column(columnName), expressionList);
        return deptIdsIn;

    }


    /**
     * 拦截sql
     */
    @Override
    public boolean willDoQuery(Executor executor, MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) throws SQLException {
        return InnerInterceptor.super.willDoQuery(executor, ms, parameter, rowBounds, resultHandler, boundSql);
    }

    @Override
    public void beforeQuery(Executor executor, MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) throws SQLException {

        List<ParameterMapping> parameterMappings = boundSql.getParameterMappings();
        PluginUtils.MPBoundSql mpBoundSql = PluginUtils.mpBoundSql(boundSql);
        mpBoundSql.sql(parserSingle(mpBoundSql.sql(), null));

    }

    /**
     * 使用JsqlParserSupport类的方法对拦截后的sql进行改造（CRUD）
     */
    @Override
    protected void processInsert(Insert insert, int index, String sql, Object obj) {
        super.processInsert(insert, index, sql, obj);
    }

    @Override
    protected void processDelete(Delete delete, int index, String sql, Object obj) {
        super.processDelete(delete, index, sql, obj);
    }

    @Override
    protected void processUpdate(Update update, int index, String sql, Object obj) {
        super.processUpdate(update, index, sql, obj);
    }

    @Override
    protected void processSelect(Select select, int index, String sql, Object obj) {

        if (!needInterceptor()) {
            return;
        }

        // 初始条件
        if (rulesCache.isEmpty() || auditRulesCache.isEmpty()) {
            initAuditRules();
            initRules();
        }

        Integer dataScopeType = getDataScopeType();
        String tableName = getTableTame(select);

        List<Integer> dataScopeTypeList = Arrays.asList(DataPermissionDict.DATA_SCOPE_DEPT_OWN, DataPermissionDict.DATA_SCOPE_DEPT_CHILDREN, DataPermissionDict.DATA_SCOPE_DEPT_SELECT, DataPermissionDict.DATA_SCOPE_OWN);

        synchronized (this) {

            for (Integer type : dataScopeTypeList) {
                if (type.equals(dataScopeType)) {

                    Set<String> keySet = rulesCache.keySet();
                    for (String key : keySet) {
                        if (tableName.equals(key)) {
                            Map<Integer, Expression> integerExpressionMap = rulesCache.get(key);
                            Expression expression = integerExpressionMap.get(type);
                            if (Objects.isNull(expression)) {
                                log.warn("{}->表达式无效--默认不查", key);
                                try {
                                    expression = createFail();
                                } catch (JSQLParserException e) {
                                    log.warn("{}->拦截失败", key);
                                }
                            }
                            execute(select, expression);
                        }
                    }

                }
            }

        }


        // 刷新
        rulesCache.clear();

    }

    /**
     * 不拦截
     *
     * @return
     */
    protected boolean needInterceptor() {

        return PermissionFlag.get();

    }

    /**
     * 数据过滤
     *
     * @param select
     * @param condition
     */
    protected void execute(Select select, Expression condition) {

        SelectBody selectBody = select.getSelectBody();
        // 如果是普通查询
        if (selectBody instanceof PlainSelect) {

            PlainSelect plainSelect = (PlainSelect) selectBody;
            // 数据过滤
            plainSelect.setWhere(plainSelect.getWhere() != null ?
                    new AndExpression(plainSelect.getWhere(),
                            condition) : condition);

        }

    }


    /**
     * 获取当前查询的表明
     *
     * @param select
     * @return
     */
    protected String getTableTame(Select select) {

        String sql = select.getSelectBody().toString();
        int index1 = sql.indexOf("FROM");
        int index2 = sql.indexOf(" ", index1);
        int index3 = sql.indexOf(" ", index2 + 1);
        if (index3 == -1) {
            index3 = sql.length();
        }
        return sql.substring(index2 + 1, index3);

    }

    /**
     * 获取当前用户的审核标识符
     *
     * @return
     */
    protected Set<Long> getPersonAuditCode() {

        log.info("当前用户角色：{}", getCurrentRoleCodes());

        List<String> currentRoleCodes = getCurrentRoleCodes();
        Set<Long> personAuditCodes = new HashSet<>();
        Set<String> keySet = auditRulesCache.keySet();
        for (String key : keySet) {
            if (currentRoleCodes.contains(key)) {
                personAuditCodes.add(Long.parseLong(String.valueOf(auditRulesCache.get(key))));
            }
        }
        return personAuditCodes;

    }

    /**
     * 获取当前用户的数据权限类型
     *
     * @return
     */
    protected Integer getDataScopeType() {
        return SecurityService.currentUser().getSysUser().getDataScopeType();
    }

    /**
     * 获取当前用户部门ID
     *
     * @return
     */
    protected Long getDeptId() {
        return SecurityService.currentUser().getSysUser().getDeptId();
    }

    /**
     * 获取当前用户所在部门及其子部门ID
     *
     * @return
     */
    protected Set<Long> getDeptChildrenIds() {
        return SecurityService.deptChildrenIds();
    }

    /**
     * 获取当前用户的ID
     *
     * @return
     */
    protected Long getUserId() {
        return SecurityService.currentUser().getSysUser().getId();
    }

    /**
     * 当前用户选择的部门
     *
     * @return
     */
    protected Set<Long> getSelectedDeptIds() {
        return SecurityService.selectedDeptIds();
    }

    /**
     * 获取当前用户所拥有的菜单
     *
     * @return
     */
    protected Set<Long> getMenuIds() {
        return SecurityService.currentMenuIds();
    }

    /**
     * 获取当前用户的角色标识符
     *
     * @return
     */
    protected List<String> getCurrentRoleCodes() {
        return SecurityService.currentUserRoles();
    }

    /**
     * 获取当前用户的用户名
     *
     * @return
     */
    protected String getCurrentUserName() {
        return SecurityService.currentUserName();
    }

    /**
     * 获取指定DeptId下的所有用户名
     *
     * @return
     */
    protected List<String> getUserNameByDeptIds() {

        return SecurityService.getUserNameByDeptId();

    }

    /**
     * 返回选定的DeptId下的所有用户
     *
     * @return
     */
    protected List<String> getUserNameBySelectDeptIds() {

        return SecurityService.getUserNameBySelectDeptId();

    }

    /**
     * 返回当前用户部门的所有用户
     *
     * @return
     */
    protected List<String> getUserNameByCurrentDept() {

        return SecurityService.getUserNameByOwn();

    }

    /**
     * 返回当前用户部门及其子部门的所有用户
     *
     * @return
     */
    protected List<String> creatorsActiveOwnDept() {

        return SecurityService.creatorsActiveOwnDept();

    }

    /**
     * 返回当前用户部门及其子部门的所有用户
     *
     * @return
     */
    protected List<String> creatorsActiveChildDept() {

        return SecurityService.creatorsActiveChildDept();

    }

    /**
     * 返回当所选部门的所有用户
     *
     * @return
     */
    protected List<String> creatorsActiveSelectDept() {

        return SecurityService.creatorsActiveSelectDept();

    }

}
